"""
Python高级特性学习 - 上下文管理器
======================

本文件演示了Python中上下文管理器的使用方法，包括自定义上下文管理器和装饰器方式。
上下文管理器用于确保资源的正确获取和释放，常用于文件操作、数据库连接等场景。
通过实现__enter__和__exit__方法或使用contextlib装饰器，可以创建自定义的上下文管理器。
"""

import os
import time
import tempfile
from contextlib import contextmanager, closing, suppress
from typing import Generator, Any


class FileHandler:
    """自定义文件处理器上下文管理器
    
    这个类演示了如何通过实现__enter__和__exit__方法来创建自定义上下文管理器。
    __enter__方法在进入with语句时调用，返回需要使用的资源。
    __exit__方法在离开with语句时调用，用于清理资源。
    """
    
    def __init__(self, filename: str, mode: str = 'r'):
        """初始化文件处理器
        
        Args:
            filename (str): 文件名
            mode (str): 文件打开模式
        """
        self.filename = filename
        self.mode = mode
        self.file = None
    
    def __enter__(self):
        """进入上下文
        
        打开文件并返回文件对象，这样在with语句中就可以直接使用文件对象。
        
        Returns:
            file: 打开的文件对象
        """
        print(f"打开文件: {self.filename}")
        self.file = open(self.filename, self.mode)
        return self.file
    
    def __exit__(self, exc_type, exc_value, traceback):
        """退出上下文
        
        关闭文件，无论是否发生异常都会执行此方法。
        参数说明：
        - exc_type: 异常类型
        - exc_value: 异常值
        - traceback: 异常 traceback 对象
        
        Returns:
            bool: 如果返回True，会抑制异常；返回False或None则不抑制异常
        """
        if self.file:
            print(f"关闭文件: {self.filename}")
            self.file.close()
        # 返回False表示不抑制异常，让异常正常传播
        return False


@contextmanager
def timer() -> Generator[None, None, None]:
    """计时器上下文管理器
    
    使用contextlib.contextmanager装饰器创建的上下文管理器。
    yield语句之前的部分相当于__enter__方法，在进入with语句时执行。
    yield语句之后的部分相当于__exit__方法，在离开with语句时执行。
    如果with语句块中发生异常，异常会在yield语句处抛出。
    """
    start_time = time.time()
    print("开始计时...")
    try:
        yield  # 这里是with语句块执行的地方
    finally:
        end_time = time.time()
        print(f"执行时间: {end_time - start_time:.4f}秒")


@contextmanager
def temporary_directory() -> Generator[str, None, None]:
    """临时目录上下文管理器
    
    创建一个临时目录，在退出时自动删除。
    通过yield返回临时目录的路径，供with语句块使用。
    
    Yields:
        str: 临时目录的路径
    """
    temp_dir = tempfile.mkdtemp()
    print(f"创建临时目录: {temp_dir}")
    try:
        yield temp_dir
    finally:
        # 清理临时目录
        import shutil
        shutil.rmtree(temp_dir)
        print(f"删除临时目录: {temp_dir}")


def database_operations_demo():
    """数据库操作演示
    
    模拟数据库连接和操作，展示上下文管理器在资源管理中的作用。
    即使在操作过程中发生异常，也能确保连接被正确关闭。
    """
    print("=== 数据库操作演示 ===")
    
    class DatabaseConnection:
        """模拟数据库连接"""
        def __init__(self, db_name: str):
            self.db_name = db_name
            self.connected = False
        
        def connect(self):
            """连接数据库"""
            print(f"连接到数据库: {self.db_name}")
            self.connected = True
        
        def disconnect(self):
            """断开数据库连接"""
            print(f"断开数据库连接: {self.db_name}")
            self.connected = False
        
        def execute(self, query: str):
            """执行查询"""
            if not self.connected:
                raise Exception("数据库未连接")
            print(f"执行查询: {query}")
            # 模拟可能的异常
            if "error" in query.lower():
                raise Exception("查询执行失败")
    
    @contextmanager
    def database_connection(db_name: str) -> Generator[DatabaseConnection, None, None]:
        """数据库连接上下文管理器"""
        db = DatabaseConnection(db_name)
        db.connect()
        try:
            yield db
        finally:
            db.disconnect()
    
    # 正常操作
    try:
        with database_connection("test_db") as db:
            db.execute("SELECT * FROM users")
            db.execute("INSERT INTO users (name) VALUES ('Alice')")
    except Exception as e:
        print(f"操作失败: {e}")
    
    # 异常处理
    try:
        with database_connection("test_db") as db:
            db.execute("SELECT * FROM users")
            db.execute("INVALID QUERY THAT CAUSES ERROR")
    except Exception as e:
        print(f"捕获异常: {e}")


def file_operations_demo():
    """文件操作演示
    
    展示不同方式的文件操作上下文管理器。
    """
    print("\n=== 文件操作演示 ===")
    
    # 1. 使用内置的文件上下文管理器
    print("1. 内置文件上下文管理器")
    with open("test.txt", "w") as f:
        f.write("Hello, World!")
    
    with open("test.txt", "r") as f:
        content = f.read()
        print(f"读取内容: {content}")
    
    # 2. 使用自定义文件处理器
    print("\n2. 自定义文件处理器")
    with FileHandler("test.txt", "r") as f:
        content = f.read()
        print(f"读取内容: {content}")
    
    # 3. 使用临时目录
    print("\n3. 临时目录操作")
    with temporary_directory() as temp_dir:
        temp_file = os.path.join(temp_dir, "temp.txt")
        with open(temp_file, "w") as f:
            f.write("临时文件内容")
        print(f"在临时目录创建文件: {temp_file}")
        # 临时目录会在退出时自动删除


def multiple_context_managers():
    """多个上下文管理器演示
    
    展示如何同时使用多个上下文管理器。
    可以使用嵌套的with语句或在同一个with语句中用逗号分隔。
    """
    print("\n=== 多个上下文管理器 ===")
    
    # 方法1: 嵌套with语句
    print("1. 嵌套with语句")
    with open("file1.txt", "w") as f1:
        with open("file2.txt", "w") as f2:
            f1.write("文件1内容")
            f2.write("文件2内容")
    
    # 方法2: 单个with语句
    print("\n2. 单个with语句")
    with open("file1.txt", "r") as f1, open("file2.txt", "r") as f2:
        content1 = f1.read()
        content2 = f2.read()
        print(f"文件1内容: {content1}")
        print(f"文件2内容: {content2}")
    
    # 清理测试文件
    os.remove("file1.txt")
    os.remove("file2.txt")


def exit_stack_demo():
    """ExitStack演示
    
    使用contextlib.ExitStack可以动态管理多个上下文管理器。
    ExitStack会在退出时按相反顺序关闭所有上下文管理器。
    """
    print("\n=== ExitStack演示 ===")
    from contextlib import ExitStack
    
    with ExitStack() as stack:
        # 动态添加上下文管理器
        f1 = stack.enter_context(open("test1.txt", "w"))
        f2 = stack.enter_context(open("test2.txt", "w"))
        
        f1.write("测试文件1")
        f2.write("测试文件2")
        
        # 可以根据条件动态添加更多上下文管理器
        if True:  # 模拟条件
            timer_ctx = stack.enter_context(timer())
            time.sleep(0.1)  # 模拟一些操作
    
    # 清理测试文件
    os.remove("test1.txt")
    os.remove("test2.txt")


def best_practices():
    """上下文管理器最佳实践
    
    提供使用上下文管理器的最佳实践建议。
    """
    print("\n=== 最佳实践 ===")
    
    # 1. 优先使用内置上下文管理器
    print("1. 优先使用内置上下文管理器")
    with suppress(FileNotFoundError):
        with open("nonexistent.txt", "r") as f:
            content = f.read()
    print("   文件不存在但未抛出异常")
    
    # 2. 使用closing处理非上下文管理器对象
    print("\n2. 使用closing处理非上下文管理器对象")
    from urllib.request import urlopen
    # 注意：实际使用时需要网络连接
    # with closing(urlopen('http://example.com')) as page:
    #     content = page.read()
    print("   closing可以将有close方法的对象转换为上下文管理器")
    
    # 3. 合理使用__exit__方法的返回值
    print("\n3. 合理使用__exit__方法的返回值")
    class ExceptionSuppressor:
        def __enter__(self):
            return self
        def __exit__(self, exc_type, exc_value, traceback):
            # 返回True抑制所有异常
            return True
    
    with ExceptionSuppressor():
        raise Exception("这个异常会被抑制")
    print("   异常被成功抑制")


def main():
    """主函数 - 运行所有示例
    
    按顺序执行所有上下文管理器相关的示例函数，
    展示其概念、实现方式和优势。
    """
    print("Python上下文管理器学习示例\n" + "=" * 30)
    
    # 基本演示
    with timer():
        time.sleep(0.1)  # 模拟一些操作
        print("执行一些操作...")
    
    # 文件操作演示
    file_operations_demo()
    
    # 数据库操作演示
    database_operations_demo()
    
    # 多个上下文管理器
    multiple_context_managers()
    
    # ExitStack演示
    exit_stack_demo()
    
    # 最佳实践
    best_practices()
    
    # 清理测试文件
    if os.path.exists("test.txt"):
        os.remove("test.txt")
    
    print("\n所有示例执行完成！")


if __name__ == "__main__":
    main()